
#include <BMap>
#include <BStyle>
#include <BFont>
#include <BFile>
#include <BLabel>
#include <BButton>
#include <BSpinBox>
#include <BPainter>
#include <BDateEdit>
#include <BTimeEdit>
#include <BLineEdit>
#include <BTextEdit>
#include <BCheckBox>
#include <BGridLayout>
#include <BVBoxLayout>
#include <BHBoxLayout>
#include <BStackLayout>
#include <BXmlDocument>
#include <BSonDocument>
#include <BFloatSpinBox>
#include <BDoubleSpinBox>
#include <BRealSpinBox>
#include <BFileDialog>
#include <BColorDialog>
#include <BMessageBox>
#include <BTreeWidget>
#include <BListWidget>
#include <BReadWrite>
#include <BIcon>
#include <BCursor>
#include <BSystem>
#include <BApplication>

#include "MainWindow.h"

MainWindow::MainWindow()
{
	//this->setFrameless(true);

	BWidget* leftWidget = new BWidget();
	{
		BGridLayout* gridLayout = new BGridLayout();
		gridLayout->setSplitable(true);
		gridLayout->setHandleSize(3);
		gridLayout->setSpacing(6);
		gridLayout->setMargin(3);
		{
			BButton* colorButton = new BButton("ColorButton");
			colorButton->setBorder(5);
			colorButton->setMargin(5);
			colorButton->setGraph(Graph_Circle_Fill);
			colorButton->setForm(Form_TextUnderIcon);
			colorButton->setTips("Open color dialog to set button color.");

			BButton* fileButton = new BButton("File\nButton");
			fileButton->setIcon(new BIcon("data/ui/fileopen.png"));
			fileButton->setIconSize(32, 32);
			fileButton->setForm(Form_TextUnderIcon);
			fileButton->setAlign(Align_BottomCenter);
			fileButton->setTips("Open file dialog to choose image for button and tree widget background.");

			BButton* messageButton = new BButton("Message\nButton");
			messageButton->setTips("Open message dialog.");

			BLabel* spinBoxLabel = new BLabel("Spinbox");
			BSpinBox* spinBox = new BSpinBox(-10000, 10000);
			spinBox->setName("spinbox");
			spinBox->setValue(1234);
			spinBox->setAlign(Align_Center);
			//spinBox->setFormulable(true);
			spinBox->setButtonShown(true);
			spinBox->setPrefix("Integer");
			spinBox->setSuffix("suffix");

			BRealSpinBox* realSpinBox = new BRealSpinBox(-10000, 10000);
			realSpinBox->setName("real-spinbox");
			realSpinBox->setValue(1234);
			realSpinBox->setAlign(Align_Center);
			realSpinBox->setFormulable(true);
			realSpinBox->setButtonShown(true);
			realSpinBox->setPrefix("Real");
			realSpinBox->setSuffix("suffix");

			BLabel* floatSpinBoxLabel = new BLabel("Float\nSpinbox");
			floatSpinBoxLabel->setAlign(Align_TopCenter);
			BFloatSpinBox* floatSpinBox = new BFloatSpinBox(-10000, 10000);
			floatSpinBox->setName("float-spinbox");
			floatSpinBox->setDecimals(6);
			floatSpinBox->setValue(5678.123f);
			floatSpinBox->setAlign(Align_Center);
			floatSpinBox->setFormulable(true);
			floatSpinBox->setButtonShown(true);
			floatSpinBox->setPrefix("Float");
			floatSpinBox->setSuffix("suffix");

			BDoubleSpinBox* doubleSpinBox = new BDoubleSpinBox(-10000, 10000);
			doubleSpinBox->setName("double-spinbox");
			doubleSpinBox->setDecimals(6);
			doubleSpinBox->setValue(5678.123);
			doubleSpinBox->setAlign(Align_Center);
			doubleSpinBox->setFormulable(true);
			doubleSpinBox->setButtonShown(true);
			doubleSpinBox->setPrefix("Double");
			doubleSpinBox->setSuffix("suffix");

			BLabel* synchroLabel = new BLabel("Synchro");
			BCheckBox* timeSnychroBox = new BCheckBox("Sync Time");
			BCheckBox* dateSnychroBox = new BCheckBox("Sync Date");

			BLabel* timeLabel = new BLabel("Time-Date");

			BTimeEdit* timeEdit = new BTimeEdit();
			//timeEdit->setSynchro(true);
			timeEdit->setTime(BSystem::LocalTime());
			//timeEdit->setReadOnly(true);

			BDateEdit* dateEdit = new BDateEdit();
			//dateEdit->setSynchro(true);
			dateEdit->setDate(BSystem::LocalDate());
			dateEdit->setAlign(Align_Center);

			gridLayout->setWidget(0, 0, colorButton);
			gridLayout->setWidget(0, 1, fileButton);
			gridLayout->setWidget(0, 2, messageButton);
			gridLayout->setWidget(1, 0, spinBoxLabel);
			gridLayout->setWidget(1, 1, spinBox);
			gridLayout->setWidget(1, 2, realSpinBox);
			gridLayout->setWidget(2, 0, floatSpinBoxLabel);
			gridLayout->setWidget(2, 1, floatSpinBox);
			gridLayout->setWidget(2, 2, doubleSpinBox);
			gridLayout->setWidget(3, 0, synchroLabel);
			gridLayout->setWidget(3, 1, timeSnychroBox);
			gridLayout->setWidget(3, 2, dateSnychroBox);
			gridLayout->setWidget(4, 0, timeLabel);
			gridLayout->setWidget(4, 1, timeEdit);
			gridLayout->setWidget(4, 2, dateEdit);
			gridLayout->setColumnStretch(1, 5);

			connect(spinBox, Signal_EditFinished, &MainWindow::slotSpinBoxEditFinished);
			connect(realSpinBox, Signal_EditFinished, &MainWindow::slotRealSpinBoxEditFinished);
			connect(floatSpinBox, Signal_EditFinished, &MainWindow::slotFloatSpinBoxEditFinished);
			connect(doubleSpinBox, Signal_EditFinished, &MainWindow::slotDoubleSpinBoxEditFinished);
			connect(colorButton, Signal_Clicked, &MainWindow::slotColorButtonClicked);
			connect(fileButton, Signal_Clicked, &MainWindow::slotFileButtonClicked);
			connect(messageButton, Signal_Clicked, &MainWindow::slotMessageButtonClicked);
		}

		BTableWidget* tableWidget = new BTableWidget();
		tableWidget->setBorder(15);
		tableWidget->setMargin(15);
		tableWidget->setRowSpacing(4);
		tableWidget->setColumnSpacing(4);
		tableWidget->setRowCount(3);
		tableWidget->setColumnCount(2);
		tableWidget->setRowLabel(0, "row0");
		tableWidget->setRowLabel(1, "row1");
		tableWidget->setRowLabel(2, "row2");
		tableWidget->setColumnLabel(0, "column0");
		tableWidget->setColumnLabel(1, "column1");
		tableWidget->setGridStyle(Grid_Crossing);
		{
			BLineEdit* itemEdit = new BLineEdit(tableWidget);
			itemEdit->setName("editer");
			itemEdit->setBorder(0);
			itemEdit->setMargin(0);
			itemEdit->setStyleSheet("margin-color:empty");
			itemEdit->hide();

			BComboBox* comboBox = new BComboBox();
			comboBox->setReadOnly(false);
			comboBox->addItem("123456789");
			comboBox->addItem("abcdefg");
			comboBox->addItem("third party");
			BButton* button = new BButton("中国软件");

			tableWidget->setItem(0, 0, comboBox);
			tableWidget->setItem(0, 1, "This is just test text");
			tableWidget->setItem(1, 0, "margin-color:empty");
			tableWidget->setItem(1, 1, button);
			tableWidget->setItem(2, 0, "Third row one");
			tableWidget->setItem(2, 1, "Third row two");

			tableWidget->item(0, 0)->setLineFeed(true);

			connect(itemEdit, Signal_EditFinished, &MainWindow::slotItemTextEdited);
			connect(itemEdit, Signal_Focused, &MainWindow::slotItemTextEditFocused);
		}

		BHBoxLayout* policyLayout = new BHBoxLayout();
		{
			BMenu* policyMenu = new BMenu();
			policyMenu->addAction("Fixed")->setAsset(Policy_Fixed);
			policyMenu->addAction("Preferred")->setAsset(Policy_Preferred);
			policyMenu->addAction("Shrinking")->setAsset(Policy_Shrinking);
			policyMenu->addAction("Expanding")->setAsset(Policy_Expanding);
			for (int i = 0; i < policyMenu->count(); i++)
			{
				policyMenu->action(i)->setCheckable(true);
			}

			BLabel* rowPolicyLabel = new BLabel("row-policy");
			rowPolicyLabel->setAlign(Align_Center);
			BButton* rowPolicyButton = new BButton(policyMenu);
			rowPolicyButton->setName("row-policy");

			BLabel* columnPolicyLabel = new BLabel("column-policy");
			columnPolicyLabel->setAlign(Align_Center);
			BButton* columnPolicyButton = new BButton(policyMenu);
			columnPolicyButton->setName("column-policy");

			policyLayout->addWidget(rowPolicyLabel);
			policyLayout->addWidget(rowPolicyButton, 10);
			policyLayout->addSpacer(10);
			policyLayout->addWidget(columnPolicyLabel);
			policyLayout->addWidget(columnPolicyButton, 10);

			connect(rowPolicyButton, Signal_Clicked, &MainWindow::slotPolicyButtonClicked);
			connect(columnPolicyButton, Signal_Clicked, &MainWindow::slotPolicyButtonClicked);
			connect(policyMenu, Signal_Triggered, &MainWindow::slotPolicyMenuTrigger);
		}

		BVBoxLayout* vlayout = new BVBoxLayout(leftWidget);
		vlayout->setSplitable(true);
		vlayout->setSpacing(16);
		vlayout->addLayout(gridLayout);
		vlayout->addWidget(tableWidget, 10);
		vlayout->addLayout(policyLayout, 0);
		connect(tableWidget, Signal_ItemDoubleClicked, &MainWindow::slotItemDoubleClicked);
	}

	BWidget* rightWidget = new BWidget();
	{
		BHBoxLayout* hlayout0 = new BHBoxLayout();
		hlayout0->setHeightPolicy(Policy_Preferred);
		{
			BLabel* fileLabel = new BLabel("file");
			_filePathComboBox = new BComboBox();
			_filePathComboBox->addItem("../../manual/htmls/BObject.html");
			_filePathComboBox->addItem("./data/scene.xml");
			_filePathComboBox->addItem("./data/scene.son");
			_filePathComboBox->setCurrentIndex(0);
			_filePathComboBox->setTips("Right click to open special file.");

			_loadFileButton = new BButton("Load");

			hlayout0->addWidget(fileLabel);
			hlayout0->addWidget(_filePathComboBox, 10);
			hlayout0->addWidget(_loadFileButton);
		}

		BWidget* stackWidget = new BWidget();
		BStackLayout* stackLayout = new BStackLayout(stackWidget);
		{
			_htmlWidget = new BHtmlWidget();
			
			_treeWidget = new BTreeWidget();
			_treeWidget->setName("tree");
			_treeWidget->setIndent(23);

			_textEdit = new BTextEdit();

			stackLayout->addWidget(_htmlWidget);
			stackLayout->addWidget(_treeWidget);
			stackLayout->addWidget(_textEdit);
		}

		BHBoxLayout* hlayout1 = new BHBoxLayout();
		hlayout1->setHeightPolicy(Policy_Preferred);
		{
			BLabel* fileNameLabel = new BLabel("fileName");
			_sheetfileNameEdit = new BLineEdit();
			_saveSheetButton = new BButton("Save");
			_saveSheetButton->setEnabled(false);

			hlayout1->addWidget(fileNameLabel);
			hlayout1->addWidget(_sheetfileNameEdit, 10);
			hlayout1->addWidget(_saveSheetButton);
		}

		_styleSheetEdit = new StyleEdit;
		_styleSheetEdit->setFileName("data/style/widget.css");
		_styleSheetEdit->load();

		BVBoxLayout* vlayout = new BVBoxLayout(rightWidget);
		vlayout->setSplitable(true);
		vlayout->setSpacing(8);
		vlayout->addLayout(hlayout0, 0);
		vlayout->addWidget(stackWidget, 100);
		vlayout->addLayout(hlayout1, 0);
		vlayout->addWidget(_styleSheetEdit, 100);

	}
	BHBoxLayout* splitLayout = new BHBoxLayout();
	splitLayout->setSpacing(10);
	splitLayout->setSplitable(true);
	splitLayout->addWidget(leftWidget);
	splitLayout->addWidget(rightWidget);
	rightWidget->setWidthPolicy(Policy_Preferred | Policy_Expanding);

	BVBoxLayout* layout = new BVBoxLayout(this);
	layout->addLayout(splitLayout);

	connect(_filePathComboBox, Signal_RightClicked, &MainWindow::slotFilePathRightClicked);
	connect(_loadFileButton, Signal_Clicked, &MainWindow::slotLoadButtonClicked);
	connect(_sheetfileNameEdit, Signal_EditFinished, &MainWindow::slotSheetFileNameEdited);
	connect(_sheetfileNameEdit, Signal_DoubleClicked, &MainWindow::slotChooseSheetFile);
	connect(_styleSheetEdit, Signal_EditFinished, &MainWindow::slotStyleSheetEdited);
	connect(_saveSheetButton, Signal_Clicked, &MainWindow::slotSaveSheetButtonClicked);
}
MainWindow::~MainWindow()
{

}

BTreeItem* MainWindow::createTreeItem(BXmlNode* node)
{
	if (node)
	{
		BTreeItem* item = new BTreeItem(node->name());
		item->setAsset(node);

		const BMap<BString, BString>& attribs = node->attribSet();
		for (auto it = attribs.begin(); it.valid(); ++it)
		{
			BString text;
			text << '[' << it.key() << " = " << it.value() << ']';
			BTreeItem* child = item->addChild(text);
			assignIcon(child);
		}

		if (node->text().size())
		{
			BTreeItem* child = item->addChild(node->text());
			assignIcon(child);
		}

		for (int i = 0; i < node->childCount(); i++)
		{
			BXmlNode* childNode = node->child(i);
			BTreeItem* childItem = createTreeItem(childNode);
			item->addChild(childItem);
		}
		assignIcon(item);
		return item;
	}
	return 0;
}
BTreeItem* MainWindow::createTreeItem(BSonNode* node)
{
	if (node)
	{
		BTreeItem* item = new BTreeItem(node->name());
		item->setAsset(node);

		const BMap<BString, BString>& attribs = node->attribSet();
		for (auto it = attribs.begin(); it.valid(); ++it)
		{
			BString text;
			text << '[' << it.key() << " = " << it.value() << ']';
			BTreeItem* child = item->addChild(text);
			assignIcon(child);
		}

		if (node->text().size())
		{
			BTreeItem* child = item->addChild(node->text());
			child->setAlign(Align_None);
			assignIcon(child);
		}

		for (int i = 0; i < node->childCount(); i++)
		{
			BSonNode* childNode = node->child(i);
			BTreeItem* childItem = createTreeItem(childNode);
			item->addChild(childItem);
		}
		assignIcon(item);
		return item;
	}
	return 0;
}
bool MainWindow::assignIcon(BTreeItem* item)
{
	const BStyle* style = this->realStyle();
	if (item->asset().valid())
	{
		item->setIcon(style->icon("node"));
		return true;
	}
	if (item->text().beginWith('[') && item->text().endWith(']'))
		item->setIcon(style->icon("attrib"));
	else
		item->setIcon(style->icon("text"));
	return true;
}

void MainWindow::slotColorButtonClicked(BObject* object)
{
	BButton* button = (BButton*)object;
	BColor color = BColorDialog::ChooseColor(0, "Choose Button Color", button->color());
	button->setColor(color);
}
void MainWindow::slotSpinBoxEditFinished(BObject* object, const BValue& value)
{
	int v = value;
	BRealSpinBox* spinbox = (BRealSpinBox*)this->find("real-spinbox");
	spinbox->setValue(v);
}
void MainWindow::slotRealSpinBoxEditFinished(BObject* object, const BValue& value)
{
	BReal v = value;
	BSpinBox* spinbox = (BSpinBox*)this->find("spinbox");
	spinbox->setValue((int)v);
}
void MainWindow::slotFloatSpinBoxEditFinished(BObject* object, const BValue& value)
{
	float v = value;
	BDoubleSpinBox* spinbox = (BDoubleSpinBox*)this->find("double-spinbox");
	spinbox->setValue(v);
}
void MainWindow::slotDoubleSpinBoxEditFinished(BObject* object, const BValue& value)
{
	double v = value;
	BFloatSpinBox* spinbox = (BFloatSpinBox*)this->find("float-spinbox");
	spinbox->setValue((float)v);
}
void MainWindow::slotFileButtonClicked(BObject* object)
{
	BButton* button = (BButton*)object;
	const BString& fileName = BFileDialog::OpenFile(this, "Open Image file", "./data");
	if (fileName.size())
	{
		BImage* image = ReadFile<BImage>(fileName);
		BIcon* icon = new BIcon(image);
		button->setIcon(icon);
		BWidget* treeWidget = this->find("tree");
		BStyle* style = treeWidget->style();
		if (!style)
		{
			style = new BStyle();
			treeWidget->setStyle(style);
		}
		style->setImage(Image_Background, image);
	}
}
void MainWindow::slotMessageButtonClicked()
{
	BMessageBox::Question(this, "Question", "Do you like this sample code ?", Button_Yes | Button_No);
}
void MainWindow::slotFilePathRightClicked()
{
	const BString& fileName = BFileDialog::OpenFile(this, "Browser file");
	if (fileName.empty())
		return;
	if (!_filePathComboBox->setCurrentText(fileName))
	{
		_filePathComboBox->addItem(fileName);
		_filePathComboBox->setCurrentText(fileName);
	}
}
void MainWindow::slotLoadButtonClicked()
{
	BString currentPath = BApplication::CurrentPath();
	const BString& fileName = _filePathComboBox->text();
	BFile file(fileName);
	if (!file.open(IO_Read))
		return;

	if (fileName.endWith(".html"))
	{
		_htmlWidget->read(file);
		_htmlWidget->show();
		return;
	}
	if (fileName.endWith(".xml"))
	{
		_treeWidget->clearItems();
		BXmlDocument document;
		BXmlNode* xmlRoot = document.read(file);
		if (!xmlRoot)
		{
			BMessageBox::Critical("Load xml", "Load xml file failed !", Button_Close);
			return;
		}
		BTreeItem* rootItem = createTreeItem(xmlRoot);
		_treeWidget->addTopItem(rootItem);
		_treeWidget->show();
		return;
	}
	if (fileName.endWith(".son") || fileName.endWith(".osc"))
	{
		_treeWidget->clearItems();
		BSonDocument document;
		while(BSonNode* sonode = document.read(file))
		{
			BTreeItem* rootItem = createTreeItem(sonode);
			_treeWidget->addTopItem(rootItem);
		}
		_treeWidget->show();
		return;
	}
}

void MainWindow::slotItemDoubleClicked(BObject* object, const BValue& value)
{
	BTableWidget* tableWidget = (BTableWidget*)object;
	BTableItem* item = value;
	if (!item)
	{
		BPoint pos = BCursor::GetPos() - tableWidget->globalPos();
		int row = tableWidget->rowAt(pos.y());
		int column = tableWidget->columnAt(pos.x());
		item = new BTableItem();
		tableWidget->setItem(row, column, item);
	}
	BLineEdit* itemEdit = (BLineEdit*)tableWidget->find("editer");
	itemEdit->setAsset(item);
	itemEdit->setAlign(item->align());
	itemEdit->setRect(item->textArea());
	itemEdit->setText(item->text());
	itemEdit->popup();
}
void MainWindow::slotItemTextEdited(BObject* object, const BValue& value)
{
	BLineEdit* edit = (BLineEdit*)object;
	BTableItem* item = object->asset();
	item->setText(value);
	edit->hide();
}
void MainWindow::slotItemTextEditFocused(BObject* object, const BValue& value)
{
	if (value == false)
	{
		BLineEdit* edit = (BLineEdit*)object;
		edit->hide();
	}
}
void MainWindow::slotPolicyButtonClicked(BObject* object, const BValue& value)
{
	BButton* button = dynamic_cast<BButton*>(object);
	BMenu* menu = button->menu();
	menu->setAsset(button);
	int policies = button->asset();
	for (int i = 0; i < menu->count(); i++)
	{
		BAction* action = menu->action(i);
		PolicyFlag policy = action->asset();
		action->setChecked(policies & policy);
	}
}
void MainWindow::slotPolicyMenuTrigger(BObject* object, const BValue& value)
{
	BMenu* menu = dynamic_cast<BMenu*>(object);
	BString text;
	int policies = 0;
	for (int i = 0; i < menu->count(); i++)
	{
		BAction* action = menu->action(i);
		if (action->checked())
		{
			PolicyFlag policy = action->asset();
			policies |= policy;
			text << action->text() << ',';
		}
	}
	text -= ',';
	BButton* button = menu->asset();
	button->setText(text);
	button->setAsset(policies);
	BTableWidget* tableWidget = (BTableWidget*)button->parent();
	if (button->name() == "row-policy")
	{
		tableWidget->setRowPolicy(policies);
	}
	if (button->name() == "column-policy")
	{
		tableWidget->setColumnPolicy(policies);
	}
}

void MainWindow::slotSheetFileNameEdited()
{
	const BString& fileName = _sheetfileNameEdit->text();
	if(fileName.size() && fileName != _styleSheetEdit->fileName())
		_saveSheetButton->setEnabled(true);
	else
		_saveSheetButton->setEnabled(false);
}
void MainWindow::slotChooseSheetFile()
{
	const BString& fileName = BFileDialog::OpenFile(this, "Open Image file", "./data");
	if(fileName.size())
	{
		_sheetfileNameEdit->setText(fileName);
		_styleSheetEdit->setFileName(fileName);
		_styleSheetEdit->load();
		BApplication::setStyleSheet(_styleSheetEdit->text());
		_saveSheetButton->setEnabled(false);
	}
}
void MainWindow::slotSaveSheetButtonClicked()
{
	_styleSheetEdit->save();
	_saveSheetButton->setEnabled(false);
}
void MainWindow::slotStyleSheetEdited(BObject* object, const BValue& value)
{
	BTextEdit* textEdit = dynamic_cast<BTextEdit*>(object);
	if (textEdit)
	{
		const BString& sheet = textEdit->text();
		BApplication::setStyleSheet(sheet);
	}
}

